/*
 * sched.c - initializes struct for task 0
 */

#include <sched.h>
#include <list.h>
#include <mm.h>
#include <io.h>

LIST_HEAD(runqueue);
LIST_HEAD(keyboardqueue);

struct protected_task_struct task[NR_TASKS]
  __attribute__((__section__(".data.task")));

struct task_struct* list_head_to_task_struct(struct list_head *l)
{
	return list_entry(l, struct task_struct, run_list);
}

struct semaphore* list_head_to_sem(struct list_head *l)
{
	return list_entry(l, struct semaphore, sem_list);
}

void init_task0(void)
{
	int i;
	pid = 0; /* Inicializar PID */

	/* Initializes paging for the process 0 adress space */
	initialize_P0_frames();
	set_user_pages();
	set_cr3();
	
	for(i = 0; i < NR_TASKS; ++i)
		task[i].t.task.pid = -1;
		
	for(i = 0; i < NR_SEM; ++i)
		sems[i].n_sem = -1;

	/* Set task_struct info */
	task[0].t.task.pid = pid;
	task[0].t.task.quantum = DEF_QUANTUM;
	task[0].t.task.tics = 0;
	task[0].t.task.total_tics = 0;
	task[0].t.task.total_trans = 0;
	INIT_LIST_HEAD(&(task[0].t.task.sem_list));

	/*Init channel table*/
	for(i = 0; i < CHTABLE_SIZE; i++)
		task[0].t.task.chtable[i].used = 0;

	/*Init channels 0 & 1*/
	task[0].t.task.chtable[0].used = 1;
	task[0].t.task.chtable[0].opened_file = &oft[0];

	directory[0].n_refs = 1;
	directory[1].n_refs = 1;

	oft[0].f = &directory[0];
	oft[0].n_refs = 1;
	oft[0].access_mode = O_RDONLY;
	oft[0].seek = 0;

	task[0].t.task.chtable[1].used = 1;
	task[0].t.task.chtable[1].opened_file = &oft[1];
	
	oft[1].f = &directory[1];
	oft[1].n_refs = 1;
	oft[1].access_mode = O_WRONLY;
	oft[1].seek = 0;

	for(i = 0; i < NUM_PAG_DATA; i++)
		task[0].t.task.data_frames[i] = pagusr_table[PAG_LOG_INIT_DATA_P0 + i].bits.pbase_addr;
	
	/* Runqueue */
	list_add(&(task[0].t.task.run_list), &runqueue);
}

/* Busca una posicion libre para un nuevo proceso */
int alloc_task() 
{
	int i;
	for(i = 0; i < NR_TASKS; i++)
	{
		if(task[i].t.task.pid < 0)
			return i;
	}
	return -1;
}

/* Busca una posicion libre para un nuevo semaforo */
int alloc_sem(int n_sem)
{
	if((n_sem >= NR_SEM) || (n_sem < 0)) return -EINVAL;
	if(sems[n_sem].n_sem < 0)
		return 1;
	return -EBUSY;
}

/* Devuelve la posicion de la tabla que tiene ese pid */
int search_pid(int pid)
{
	int i;
	for(i = 0; i < NR_TASKS; ++i)
	{
		if(task[i].t.task.pid == pid)
			return i;
	}
	return -1;
}

struct task_struct* current()
{
	int reg_esp;
	__asm__("movl %%esp, %0" : "=g"(reg_esp));
	return (struct task_struct *)(reg_esp & 0xFFFFF000);
}

void task_switch(union task_union * t)
{
	int pag;

	/* Actualizar TSS para que apunte a la pila de t */
	tss.esp0 = (DWord) &(t->stack[KERNEL_STACK_SIZE]);

	/* Actualizar TP */
	for (pag = 0; pag < NUM_PAG_DATA; pag++)
		set_ss_pag((unsigned)(PAG_LOG_INIT_DATA_P0 + pag), (unsigned)(t->task.data_frames[pag]));

	/* Flush TLB */
	set_cr3();

	/* Cambiar a la pila del nuevo proceso */
	__asm__("movl %0, %%esp\n"
		"subl $64, %%esp"
		: : "g"(tss.esp0));


	/* EOI? */
	if(is_eoi)
	{
		__asm__("movb $0x20, %al\n" "outb %al, $0x20");
		is_eoi = 0;
	}

	/* Restaurar los registros: RESTORE_ALL */
	__asm__(	"popl %ebx\n"
    			"popl %ecx\n"
    			"popl %edx\n"
    			"popl %esi\n"
    			"popl %edi\n"
    			"popl %ebp\n"
    			"popl %eax\n"
    			"popl %ds\n"
    			"popl %es\n"
    			"popl %fs\n"
    			"popl %gs");

	/* IRET */
	__asm__("iret");
}

/* Scheduling */
void scheduling()
{
	union task_union * next;

	/* Seleccionar siguiente proceso para pasar a run */
	next = (union task_union *) list_head_to_task_struct(runqueue.next);

	/* Actualizar variables del proceso */
	next->task.tics = 0;
	next->task.total_trans++;

	/* Cambio de contexto */
	task_switch(next);
}

